package com.xhtt.modules.task.schedule;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xhtt.common.utils.Constant;
import com.xhtt.common.utils.DateUtils;
import com.xhtt.modules.cfg.entity.CfgIsland;
import com.xhtt.modules.cfg.entity.CfgStand;
import com.xhtt.modules.cfg.service.CfgIslandService;
import com.xhtt.modules.msg.entity.MsgInfo;
import com.xhtt.modules.msg.service.MsgUserService;
import com.xhtt.modules.sys.entity.SysDeptEntity;
import com.xhtt.modules.sys.entity.SysTempTeam;
import com.xhtt.modules.sys.entity.SysUserEntity;
import com.xhtt.modules.sys.entity.SysUserProduct;
import com.xhtt.modules.sys.service.SysDeptService;
import com.xhtt.modules.sys.service.SysTempTeamService;
import com.xhtt.modules.sys.service.SysUserProductService;
import com.xhtt.modules.sys.service.SysUserService;
import com.xhtt.modules.task.entity.TaskCarPlan;
import com.xhtt.modules.task.entity.TaskCarTask;
import com.xhtt.modules.task.model.car.UserIdProdFreeTime;
import com.xhtt.modules.task.model.car.WaitingPlanModel;
import com.xhtt.modules.task.service.TaskCarPlanService;
import com.xhtt.modules.task.service.TaskCarTaskService;
import com.xhtt.modules.task.service.TruckInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Component
public class RowCarSchedule {

    @Autowired
    private TaskCarPlanService taskCarPlanService;

    @Autowired
    private TaskCarTaskService taskCarTaskService;
    @Autowired
    private SysUserProductService sysUserProductService;
    @Resource
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private MsgUserService msgUserService;
    @Autowired
    private SysDeptService sysDeptService;
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private CfgIslandService islandService;
    @Autowired
    private SysTempTeamService tempTeamService;
    @Autowired
    private TruckInfoService truckInfoService;
    @Resource
    private TransactionTemplate transactionTemplate;

//    private Lock lock = new ReentrantLock(true);

    private List<UserIdProdFreeTime> getFreeUser() {
        List<UserIdProdFreeTime> upfs = new ArrayList<>();
        SysDeptEntity team = sysDeptService.getOne(new LambdaQueryWrapper<SysDeptEntity>().eq(SysDeptEntity::getType, 2).eq(SysDeptEntity::getName, Constant.ZCZ).select(SysDeptEntity::getDeptId));
        if (null == team) {
            log.error("没有装车组");
        } else {
            //获取组内上班的人
            //新加了一片休息区 3，里面的人上班了也不用干活，所以要排除掉
            List<SysUserEntity> userList = sysUserService.list(new LambdaQueryWrapper<SysUserEntity>().eq(SysUserEntity::getTeamId, team.getDeptId()).eq(SysUserEntity::getLoginStatus, 1).ne(SysUserEntity::getLoadAreaType, 3).select(SysUserEntity::getUserId, SysUserEntity::getLoadAreaType));
            //还要获取装车组内的临时人员
            List<SysTempTeam> tempTeamList = tempTeamService.list();//只有装车组有临时组，所以全取
            if (CollectionUtils.isNotEmpty(tempTeamList)) {
                List<Integer> tempUserIds = tempTeamList.stream().map(t -> t.getUserId()).collect(Collectors.toList());
                List<SysUserEntity> tempUserList = sysUserService.list(new LambdaQueryWrapper<SysUserEntity>().in(SysUserEntity::getUserId, tempUserIds).eq(SysUserEntity::getLoginStatus, 1).ne(SysUserEntity::getLoadAreaType, 3).select(SysUserEntity::getUserId, SysUserEntity::getLoadAreaType));
                if (CollectionUtils.isNotEmpty(tempUserList)) {
                    userList.addAll(tempUserList);
                }
            }
            if (CollectionUtils.isNotEmpty(userList)) {

                userList.forEach(sysUserEntity -> {
                    UserIdProdFreeTime upt = new UserIdProdFreeTime();
                    upt.setUserId(sysUserEntity.getUserId().intValue());
                    upt.setLoadAreaType(sysUserEntity.getLoadAreaType());
                    upfs.add(upt);
                });
                //获取每个人的操作产品
                List<SysUserProduct> ups = sysUserProductService.list(new LambdaQueryWrapper<SysUserProduct>().in(SysUserProduct::getUserId, userList.stream().map(SysUserEntity::getUserId).collect(Collectors.toList())));
                for (UserIdProdFreeTime upf : upfs) {
                    List<Integer> productIds = ups.stream().filter(p -> p.getUserId().intValue() == upf.getUserId().intValue()).map(p -> p.getProductId()).collect(Collectors.toList());//获取单个人能操作的产品ids
                    upf.setProductIds(productIds);//获取每个人的操作产品
                    //获取每个人的空闲时间
                    LocalDateTime time = taskCarTaskService.getUserFinishTime(upf.getUserId());
                    if (LocalDateTime.MIN.equals(time)) {
                        upf.setFreeTime(Long.MAX_VALUE);//没做过任务
                    } else if (null == time) {
                        upf.setFreeTime(-1);//没空
                    } else {
                        LocalDateTime now = LocalDateTime.now();
                        Duration d = Duration.between(time, now);
                        long mins = d.toMinutes();//相差分钟数
                        upf.setFreeTime(mins);
                    }
                }
                //按空闲时间长短倒序
                Collections.sort(upfs, (o1, o2) -> {
                    if (o1.getFreeTime() > o2.getFreeTime()) {
                        return -1;
                    } else {
                        return 1;
                    }
                });
            }
        }
        return upfs;
    }

    /**
     * 每1分钟扫描 一次是 自动排车
     */
    @Scheduled(fixedDelay = 1 * 60 * 1000)
    public void autoRowCar() {
        //但是每个台只能有一个等待最久的车进入安排队列，所以group by一下
        List<WaitingPlanModel> allWaitingPlanList = taskCarPlanService.getAllWaitingPlans();//拿到所有待装车的plan
        if (allWaitingPlanList.size() < 1) {
            log.info("没任务");
            return;//说明没任务
        }
        //获取人一堆(包含人的空闲时间)
        List<UserIdProdFreeTime> freeUserList = getFreeUser();
        //去掉没空的人
        freeUserList.removeIf(userIdProdFreeTime -> userIdProdFreeTime.getFreeTime() <= 0);
        if (CollectionUtils.isEmpty(freeUserList)) {
            log.info("没闲人");
//            return; 不能return 因为一个人可以操作2个台 可能另一个台是空的，那这个人还是能再加任务的
        }
        //遍历所有的任务，找人来做
        for (WaitingPlanModel model : allWaitingPlanList) {
            Integer standId = model.getStandId();
            Integer islandId = model.getIslandId();
            Integer productId = model.getProductId();
            Integer taskId = model.getTaskId();
            //看看这个台上有没有正在做的任务，有的话就啥也不做
            int loadingCount = taskCarTaskService.getLoadingCount(standId);
            if (loadingCount > 0) {
                continue;
            } else {
                TaskCarPlan carPlan = taskCarPlanService.getById(model.getPlanId());
                //还要获取装车台名字
//                CfgStand stand = cfgStandService.getOne(new LambdaQueryWrapper<CfgStand>().eq(CfgStand::getId, carPlan.getStageId()).select(CfgStand::getName));
                carPlan.setStageName(model.getStandName());

                //看看台上有没有人
                CfgIsland island = islandService.getById(islandId);
                Integer userId = island.getUserId();
                int loginStatus = 0;
                if (null != userId) {
                    SysUserEntity user = sysUserService.getOne(new LambdaQueryWrapper<SysUserEntity>().eq(SysUserEntity::getUserId, userId).select(SysUserEntity::getUserId, SysUserEntity::getLoginStatus));
                    loginStatus = user.getLoginStatus();
                }
                if (null == userId || loginStatus == 0)//如果没人或者这个人下班了，那就重新找个人了
                {
                    setUserToTask(freeUserList, productId, island, taskId, carPlan);
                } else {// 如果有人
                    //看下他是不是可以操作这个产品
                    List<SysUserProduct> ups = sysUserProductService.list(new LambdaQueryWrapper<SysUserProduct>().eq(SysUserProduct::getUserId, userId));
                    List<Integer> operProductIds = new ArrayList<>();
                    if (CollectionUtils.isNotEmpty(ups)) {
                        operProductIds = ups.stream().map(p -> p.getProductId()).collect(Collectors.toList());
                    }
                    if (operProductIds.contains(0) || operProductIds.contains(productId))//可以操作的话，就把任务给他呗
                    {
                        TaskCarTask task = new TaskCarTask();
                        task.setId(taskId);
                        task.setUserId(userId);
                        taskCarTaskService.updateById(task);//把这个任务给这个人
                        //发消息
                        taskExecutor.submit(() -> {
                            List<Integer> userIds = new ArrayList<>();
                            userIds.add(userId);
                            MsgInfo msg = new MsgInfo("排车任务", "排车任务", "待装车", JSON.toJSONString(carPlan));
                            msgUserService.insertBatch(msg, userIds);
                        });

                        //然后在候选人列表中去掉这个人（虽然只有空闲的人才会在列表中，但是这里就不去判断了直接遍历了清一遍）
                        freeUserList.removeIf(userIdProdFreeTime -> userIdProdFreeTime.getUserId().intValue() == userId);

                    } else {//如果不能操作的话
                        //先看他是否空闲，空闲的话就重新找人来干，跟上面的  （如果没人或者这个人下班了，那就重新找个人了）一样的逻辑
                        long count = freeUserList.stream().filter(userIdProdFreeTime -> userIdProdFreeTime.getUserId().intValue() == userId).count();
                        if (count > 0) {
                            setUserToTask(freeUserList, productId, island, taskId, carPlan);
                        }
                        //如果不是空闲的，那就什么也不做
                        else {
                            continue;
                        }

                    }
                }

            }

        }
    }

    private void setUserToTask(List<UserIdProdFreeTime> freeUserList, Integer productId, CfgIsland island, Integer taskId, TaskCarPlan carPlan) {
        Iterator<UserIdProdFreeTime> it = freeUserList.iterator();
        while (it.hasNext()) {
            UserIdProdFreeTime upf = it.next();
            if (upf.getFreeTime() > 0 && (upf.getProductIds().contains(0) || upf.getProductIds().contains(productId))
                    && upf.getLoadAreaType().intValue() == island.getLoadAreaType().intValue())//如果这个人空闲 并且 （是全能的 或者正好这车的产品都能操作）并且这个人和岛在同一个区域，那就让他去这个岛吧
            {
                transactionTemplate.execute(transactionStatus -> {
                            TaskCarTask task = new TaskCarTask();
                            try {
                                island.setUserId(upf.getUserId());
                                islandService.removeUser(upf.getUserId());//把这个人从其他岛上下来
                                islandService.updateById(island);//然后把这个人放到这个岛上
                                task.setId(taskId);
                                task.setUserId(upf.getUserId());
                                taskCarTaskService.updateById(task);//把这个任务给这个人

                                it.remove();//列表里去掉这个人

                                //发消息
                                taskExecutor.submit(() -> {
                                    MsgInfo msg = new MsgInfo("排车任务", "排车任务", "待装车", JSON.toJSONString(carPlan));
                                    List<Integer> userIds = new ArrayList<>();
                                    userIds.add(upf.getUserId());
                                    msgUserService.insertBatch(msg, userIds);
                                });
                            } catch (Throwable e) {
                                e.printStackTrace();
                                transactionStatus.setRollbackOnly();
                                return null;
                            }
                            return task;
                        }
                );
                break;
            }
        }
    }

    /**
     * 每4分钟扫描 一次,累计等待时间超过1小时 要发消息给班长
     */
    @Scheduled(fixedDelay = 4 * 60 * 1000)
    public void waitingCarMsg() {
        //先查一下 有多少等待超时了
        List<TaskCarPlan> waitingTasks = taskCarTaskService.getAllWaitingTasks();

        //发消息
        taskExecutor.submit(() -> {
            if (CollectionUtils.isNotEmpty(waitingTasks)) {
                //找到装车组的狗班长
                SysDeptEntity team = sysDeptService.getOne(new LambdaQueryWrapper<SysDeptEntity>().eq(SysDeptEntity::getName, Constant.ZCZ).eq(SysDeptEntity::getType, 2).select(SysDeptEntity::getDeptId));
                if (null != team) {
                    SysUserEntity leader = sysUserService.getTeamLeader(team.getDeptId());
                    if (null != leader) {
                        for (TaskCarPlan plan : waitingTasks) {
                            MsgInfo msg = new MsgInfo("排车任务", "排车任务", "累计等待时间超过1小时", JSON.toJSONString(plan));
                            List<Integer> userIds = new ArrayList<>();
                            userIds.add(leader.getUserId().intValue());
                            msgUserService.insertBatch(msg, userIds);
                        }
                        List<Integer> taskIds = waitingTasks.stream().map(taskCarPlan -> taskCarPlan.getTaskCarTaskId()).collect(Collectors.toList());
                        TaskCarTask tt = new TaskCarTask();
                        tt.setMsgStatus(1);
                        taskCarTaskService.update(tt, new LambdaQueryWrapper<TaskCarTask>().in(TaskCarTask::getId, taskIds));//更新为已发送状态
                    }

                }

            }

        });
    }

    /**
     * 派车
     *
     * @param userId
     * @param standList
     */
//    @Async
    public synchronized void pushRowCarTask(Integer userId, List<CfgStand> standList) {
        for (CfgStand stand : standList) {
            //看看这个台有没有在装车的，没有的话就给他推一个车
            int loadingCount = taskCarTaskService.getLoadingCount(stand.getId());
            if (0 == loadingCount) {
                //推车之前看看有没有等待的车，有的话给他推第一辆
                TaskCarPlan p = taskCarTaskService.getWaitingTask(stand.getId());
                if (null != p) {
                    //有车，然后在看看这个车的产品 这个人能不能操作，能的话就分配给他了
                    //获取这个人的可操作产品
                    List<SysUserProduct> ps = sysUserProductService.list(new LambdaQueryWrapper<SysUserProduct>().eq(SysUserProduct::getUserId, userId));
                    if (CollectionUtils.isNotEmpty(ps)) {
                        List<Integer> productIds = ps.stream().map(product -> product.getProductId()).collect(Collectors.toList());
                        if (productIds.contains(0) || productIds.contains(p.getProductId()))//如果这个人是全能的 或者能操作这个产品 ，那就给他任务
                        {
                            TaskCarTask task = new TaskCarTask();
                            task.setId(p.getTaskCarTaskId());
                            task.setUserId(userId);
                            taskCarTaskService.updateById(task);

                            //发消息
                            taskExecutor.submit(() -> {
                                MsgInfo msg = new MsgInfo("排车任务", "排车任务", "待装车", JSON.toJSONString(p));
                                List<Integer> userIds = new ArrayList<>();
                                userIds.add(userId);
                                msgUserService.insertBatch(msg, userIds);
                            });
                        }
                    }

                }
            }

        }
    }

    /**
     * 获取信的排车信息
     */
    @Scheduled(fixedDelay = 2 * 60 * 1000)
    public void getCarFromTAS() {
        String maxDate = truckInfoService.getMaxDate();
        if (StringUtils.isEmpty(maxDate)) {
            maxDate = LocalDateTime.now().format(DateUtils.dtf);
        }
        truckInfoService.getDataFromTAS(maxDate);//获取基础数据
        //更新重车过磅时间和打印磅单时间 这一般是在装车完成后才有的时间
        maxDate = truckInfoService.getMaxPrintDate();
        if (StringUtils.isEmpty(maxDate)) {
            maxDate = LocalDateTime.now().format(DateUtils.dtf);
        }
        truckInfoService.getPrintDataFromTAS(maxDate);//获取重车过磅时间和打印磅单时间数据
    }
}
